/*******************************************************************************
AVR8sa1os101.c

===============================================================================
Copyright (c) 2010, Centeye, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Centeye, Inc. nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL CENTEYE, INC. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
===============================================================================


AVR8 standalone sensor operating system

This code supports basic IO functions for the 8-bit AVR sensor. When finalized this 
code will generally be slow-moving except for major revisions or bug fixes. In the 
future this code may be split into two sections, one for basic IO and I2C and that 
may not change and another for more liquid parts. The former would support, for 
example, the basic functions that allow programming/updating of firmware over the 
I2C link.

Origin:
Adapted from AtmelInterface by Travis Young, which was adapted from Craig's
comm handlers.

Change Log:
2010 May 21, Friday, birth
2010 August 6, Friday, updated to support LED on port A4, documentation
Version 101:
2010 August 9, Monday, added I2C address change!
******************************************************************************/


#include "AVR8sa1os101.h"
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/eeprom.h>
#include "F64PlusInterface.h"
#include "I2CSupport.h"
#include "ApplicationPrototype.h"



//////////////////////////////////////////////////////////////////////////////
// ATMEL_INIT: initialize the AVR8 including port directions
//
// This function is called at sensor power up.
//
// Possible changes: read I2C address from EEPROM
//////////////////////////////////////////////////////////////////////////////
void atmel_init(void)
{
	// Set port directions
	DDRA=0x10;	//Port A is all input except for bit 4 (LED) which is output.
	DDRB=0x07;	//set WNR, CS, and SYP to output, rest to input
	DDRD=0xFF;	//set io0-7 as outputs
	PRR0=0x77;	//power reduction register - shut down all except TWI and TC1

	// Set up timer to calculate FPS.
	// Note: At current time we are not sure of accuracy of FPS computations
	TCCR1B |= ((1<<CS10)|(1<<CS12));
	
	// Initialize slave address to contents of 0x0000 in eeprom. This is
	// the 7-bit address. If the stored value is greater than 63 then
	// we  just use 0x10 as a default
	unsigned char addr = eeprom_read_byte((unsigned char*)EEP_I2C_address);
	if (addr<=63)
		twi_slave_init(addr);
	else
		twi_slave_init(0x10);

	// Enable interrupts
	sei();								
}



//////////////////////////////////////////////////////////////////////////////
// OS_INIT: other initializations to operating system including comm handlers
//
// This function is called at sensor power up. Currently this function just
// initializes a diagnostic data structure and nothing else. If major changes
// are made to the operating system, then this function may be updated.
//
// Possible changes: none
//////////////////////////////////////////////////////////////////////////////
void os_init(void)
{
	// initialize "last command" structure. This is a data structure that
	// stores the most recent commands read and is used for diagnostics
	// and debugging.
	last_cmd.locked = 0;
	last_cmd.read_length = 6;
	last_cmd.read[0] = 0;
	last_cmd.read[1] = 0;
	last_cmd.read[2] = 0;
	last_cmd.read[3] = 0;
	last_cmd.read[4] = 0;
	last_cmd.read[5] = 0;
	
	// initialize SMBus state variables
	smbus_last_command = 0;
	smbus_start_partial = 0;
	smbus_partial_left = 0;
}


//////////////////////////////////////////////////////////////////////////////
// PROCESS_COMMANDS: Given a command received by I2C, process it, for example
// by adjusting biases or other parameters
//
// This function is called by function Application_MainLoop in the 
// application firmware. This function processes all commands that are 
// pending in the command buffer structure cmdbuf. Operating system level
// commands are processed here. Vision chip and application commands are
// processed by other functions called from this function.
//
// Command numbers indicate the type of command.
// Between 0 and 29: These are operating system commands processed here.
// Between 30 and 59: These are vision chip commands processed in the
// appropriate vision chip function, for example process_F64_command.
// [NOTE: UPDATE TO SINGLE NAME]
// Between 60 and 255: These are application commands processed in the
// application function process_application_command.
//
//
// TO DO: ==== Rename vision chip process commands to single name so no
// changes need to be made to this function when switching vision chips
//////////////////////////////////////////////////////////////////////////////
int process_commands(void)
{
	int success=0;
	unsigned char commands_executed; // Counter for how many commands were executed 
									 // this time
	unsigned char command[CMDBUF_WIDTH]; // This array stores the current command 
										 // being processed

	// if there are no commands pending, then return
	if (cmdbuf.num_cmd_pending == 0)
		return success;
	commands_executed = 0;
	
	while ( cmdbuf.num_cmd_pending && (commands_executed <= CMDBUF_DEPTH) ) {  

		// load command[] with a command to be processed
		command[0] = cmdbuf.cmd[ cmdbuf.next_in_queue ];
		command[1] = cmdbuf.arg1[ cmdbuf.next_in_queue ];


		if (command[0]<=29) {
			// current command is an operating system command and processed here
			switch(command[0])	//switch on command type
			{	
				// THESE ARE OPERATING SYSTEM COMMANDS
				//reset command
				case COMM_CMD_RESET:	
					//	if(command[1]==1)
						//MasterReset();	//haven't figured out how to do this yet
					break;

				//change TWI address
				//note the address must be <= 63, but the upper two bits of the
				//argument must be 01. This prevents accidental setting.
				case COMM_CMD_SET_ADDR:	
					if ((command[1]&0xC0) == 0x40) {
						unsigned char addr = command[1] & 0x3F;
						eeprom_update_byte((unsigned char*)EEP_I2C_address,addr);
						twi_slave_init(addr);
					}
					//if((command[1]>=0x02)&&(command[1]<=0x9E))
					//	NewI2CAddr = (command[1]);		//not set up yet
					break;

				//change the current array to transmit (pixels, OF, FPS, etc)	
				case COMM_CMD_ATT:
					process_transmit(command[1]); // Ready to use new array
					break;

				default:
					break;
			} // switch

		} else if (command[0]<=59) {
			// current command is a vision chip command
			process_F64_command(command[0],command[1]);
		} else {
			// current command is an application prototype command
			process_application_command(command[0],command[1]);
		} // if

		// store the last command processed for diagnostics if necessary
		cmdbuf.last_command_processed = command[0];
		cmdbuf.last_argument_processed = command[1];
		// ==== can we delete the above two lines?


		// clear command buffer- not really necessary but helpful for debugging
		cmdbuf.cmd[ cmdbuf.next_in_queue ] = cmdbuf.arg1[ cmdbuf.next_in_queue ] = 0;
		cmdbuf.next_in_queue++;
		if (cmdbuf.next_in_queue == CMDBUF_DEPTH)
			cmdbuf.next_in_queue = 0;
		commands_executed++;
		cmdbuf.num_cmd_pending--;

		// update last_cmd IO data structure for debugging/diagnostics
		if (last_cmd.locked == 0) {
			last_cmd.read[0] = command[0];
			last_cmd.read[1] = command[1];
			last_cmd.read[2] = cmdbuf.num_cmd_pending;
			last_cmd.read[3] = cmdbuf.overflow;
			last_cmd.read[4] = cmdbuf.next_in_queue;
			last_cmd.read[5] = cmdbuf.queue_end;
		}


	} // while

	return success;
}

//////////////////////////////////////////////////////////////////////////////
// PROCESS_TRANSMIT: Prepare arrays for transmission over I2C to master
//
// This function is called by the above process_commands function whenever
// a command is issued to change the ATT (array to transmit) variable. 
// array_to_transmit between 0 and 29: Operating system array
// array_to_transmit between 30 and 59: vision chip array
// array_to_transmit between 60 and 255: application array
// Essentially what this command does is to set pointers in the transmit buffer
// structure xmtbuf to the current array to be read.
//
// Possible changes: None currently planned, unless we add double buffering
//////////////////////////////////////////////////////////////////////////////
unsigned char process_transmit(unsigned char array_to_transmit)
{
	unsigned char byte=0;


	if (array_to_transmit <= 29) {
		// The array to transmit is an operating system array
		switch(array_to_transmit) {
			// Enumeration: Nine byte identifier
			case COMM_ATT_ENUM:
				xmtbuf.comm_att = COMM_ATT_ENUM;
				xmtbuf.read = enumeration.read;
				xmtbuf.read_length = enumeration.read_length;
				xmtbuf.locked = &(enumeration.locked);
				break;

			case COMM_ATT_ENUM_DATA_SETS:
				byte=0xAA;	//legacy. currently does nothing
				break;

			default:
				byte=0xAA;
				break;

		} // switch
	} else if (array_to_transmit <= 59) {
		//  The array to transmit is a vision chip array
		process_F64_transmit(array_to_transmit);
	} else {
		// The array to transmit is an application firmware array
		process_application_transmit(array_to_transmit);
	}

	xmtbuf.transmit_counter = 0;		// redundant instruction
	*(xmtbuf.locked) = 0;				// redundant instruction

	return 0;
}
